# -*- coding: utf-8 -*-
# @Time : 2021/05/18
# @Author : ricky
# @File : ruoyivue.py
# @Software: vscode
"""
核心修改类 RuoYi-Vue版本
"""
import os

from core import base
from utils.exception import catch_except


class RuoYiVueAuto(base.BaseCore):

    @catch_except
    def start(self):
        self.series = 'RuoYi-Vue-Auto'
        # 查找项目根目录
        self.rootpath, self.rootname = self.find_root_dir(
            self.targetdir)
        # 读取配置文件中的模块
        self.module_name_list = list(eval(self.templatedict[self.series + '.default_module_name_tuple']))
        # 查找模块
        self.detect_module(self.rootpath, None)
        self.messagehandle('获取模块：{}'.format(self.module_name_list))
        # 1.修改站点名称
        self.messagehandle('正在修改标题和站点名称...')
        self.__alter_site_name_and_title()
        self.messagehandle('站点名称和标题修改完成!')
        # 2.修改包名和项目名
        self.messagehandle('正在修改包名和项目名...')
        self.__alter_package_name_and_project_name(self.rootpath)
        self.messagehandle('包名和项目名修改完成!')
        # 3.修改pom.xml文件
        self.messagehandle('正在修改pom.xml...')
        self.__alter_pom_xml()
        self.messagehandle('pom.xml修改完成!')
        # 4.修改目录结构
        self.messagehandle('正在修改目录结构...')
        self.__alter_project_dir()
        self.messagehandle('目录结构修改完成!')

        if len(self.exceptions) > 0:
            self.messagehandle('\r发现有异常信息')
            self.messagehandle('-------------------\n\r')
            for e in self.exceptions:
                self.messagehandle(e)
            self.messagehandle('\r----------------------')

    def detect_module(self, path, parent):
        files = os.listdir(path)
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        for filename in files:
            filepath = os.path.join(path, filename)
            flag = filename.startswith(default_project_name + '-')
            if flag and os.path.isdir(filepath):
                if parent is None:
                    self.detect_module(filepath, filename)
                    # 需要修改模块下的pom.xml文件，因此含有子模块的模块也需要加入集合
                    self.module_name_list.append(filename)
                    # if len(list(filter(lambda x: x.startswith(filename), self.module_name_list))) == 0:
                    #     self.module_name_list.append(filename)
                else:
                    module = parent + '#' + filename
                    self.detect_module(filepath, module)
                    self.module_name_list.append(module)
                    # if len(list(filter(lambda x: x.startswith(parent + '#' + filename), self.module_name_list))) == 0:
                    #     self.module_name_list.append(module)

    def __alter_site_name_and_title(self):
        """修改站点名称和网站标题"""
        ntuple = tuple(
            eval(self.templatedict[self.series + '.site_resources_path_tuple']))
        default_site_name = self.templatedict[self.series +
                                              '.default_site_name']
        for item in ntuple:
            filepath = os.path.join(self.rootpath,
                                    item.replace('#', os.path.sep))
            if os.path.exists(filepath):
                try:
                    with open(filepath, 'r',
                              encoding=self.DEFAULT_ENCODING) as srcfile, open(
                        '%s.bak' % filepath,
                        'w',
                        encoding=self.DEFAULT_ENCODING) as desfile:
                        for line in srcfile:
                            if default_site_name in line:
                                line = line.replace(
                                    default_site_name, self.sitename)
                            if '若依后台管理系统' in line:
                                line = line.replace('若依后台管理系统', self.sitename)
                            desfile.write(line)
                    # 移除旧文件
                    os.remove(filepath)
                    # 重命名备份文件为新文件
                    os.rename('%s.bak' % filepath, filepath)
                except Exception as err:
                    self.exceptionhandle(
                        '修改站点名称和网站标题异常\n修改文件：{}\n异常信息：{}'.format(
                            filepath, err))

    def __alter_package_name_and_project_name(self, rootpath):
        """修改包名和项目名称"""
        files = os.listdir(rootpath)
        default_package_name = self.templatedict[self.series +
                                                 '.default_package_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        default_dir_name = self.templatedict[self.series +
                                             '.default_dir_name']


        for filename in files:
            filepath = os.path.join(rootpath, filename)
            if os.path.isdir(filepath):
                self.__alter_package_name_and_project_name(filepath)
            else:
                if (filename.endswith('.java')
                        or filename.endswith('.yml')
                        or filename.endswith('.properties')
                        or filename.endswith('Mapper.xml')
                        or filename.endswith('logback.xml')
                        or filename.endswith('banner.txt')
                        or filename.endswith('.vm')
                        or filename.endswith('.bat')
                        or filename.endswith('.sh')
                        or filename.endswith('.imports')
                        or filename.endswith('.factories')):
                    try:
                        encoding = self.get_encoding(filename)
                        with open(filepath, 'r',
                                  encoding=encoding) as srcfile, open(
                            '%s.bak' % filepath,
                            'w',
                            encoding=encoding) as desfile:
                            self.messagehandle('正在修改：' + filename)
                            for line in srcfile:
                                if default_package_name in line:
                                    line = line.replace(
                                        default_package_name,
                                        self.packagename)
                                # .分隔符被转义的情况
                                if default_package_name.replace('.', '\\\\.') in line:
                                    line = line.replace(
                                        default_package_name.replace('.', '\\\\.'),
                                    self.packagename.replace('.', '\\\\.'))
                                if default_project_name in line:
                                    line = line.replace(
                                        default_project_name,
                                        self.projectname)
                                if default_dir_name in line:
                                    line = line.replace(
                                        default_dir_name,
                                        self.projectdirname)
                                if self.configdict['config.enable'] == 'True':
                                    if filename.endswith('.yml'):
                                        line = self.__check_yml_config(
                                            line, filename)
                                desfile.write(line)
                        # 移除旧文件
                        os.remove(filepath)
                        # 重命名备份文件为新文件
                        new_filepath = filepath
                        if default_dir_name in filename:
                            new_filepath = os.path.join(rootpath, filename.replace(
                                default_dir_name,
                                self.projectdirname))
                        os.rename('%s.bak' % filepath, new_filepath)
                    except Exception as err:
                        self.exceptionhandle(
                            '修改包名和项目名称异常\n修改文件：{}\n异常信息：{}'.format(
                                filepath, err))

    def __check_yml_config(self, line, filename):
        """
        检测yml配置文件

        参数:
            line (str): 行
            filename (str): 文件名
        """
        if 'localhost:3306/ry-vue' in line and filename == 'application-druid.yml':
            line = self.__alert_yml_config(line, 'mysql_ip_port_name')
        if 'username: root' in line and filename == 'application-druid.yml':
            line = self.__alert_yml_config(line, 'mysql_username')
        if 'password: password' in line and filename == 'application-druid.yml':
            line = self.__alert_yml_config(line, 'mysql_password')
        if 'host: localhost' in line and filename == 'application.yml':
            line = self.__alert_yml_config(line, 'redis_host')
        if 'port: 6379' in line and filename == 'application.yml':
            line = self.__alert_yml_config(line, 'redis_port')
        if 'password: \n' in line and filename == 'application.yml':
            line = self.__alert_yml_config(line, 'redis_password')
        return line

    def __alert_yml_config(self, line, type_):
        """
        修改yml配置文件

        参数:
            line (str): 行
            type_ (str): 修改类型
        """
        if type_ == 'mysql_ip_port_name':
            mysql_ip = self.configdict['database.ip']
            mysql_port = self.configdict['database.port']
            mysql_name = self.configdict['database.name']
            return line.replace('localhost:3306/ry-vue',
                                mysql_ip + ':' + mysql_port + '/' + mysql_name)
        if type_ == 'mysql_username':
            mysql_username = self.configdict['database.username']
            return line.replace('username: root',
                                'username: ' + mysql_username)
        if type_ == 'mysql_password':
            mysql_password = self.configdict['database.password']
            return line.replace('password: password',
                                'password: ' + mysql_password)
        if type_ == 'redis_host':
            redis_ip = self.configdict['redis.ip']
            return line.replace('host: localhost', 'host: ' + redis_ip)
        if type_ == 'redis_port':
            redis_port = self.configdict['redis.port']
            return line.replace('port: 6379', 'port: ' + redis_port)
        if type_ == 'redis_password':
            redis_password = self.configdict['redis.password']
            return line.replace('password: \n',
                                'password: ' + redis_password + '\n')
        return line

    def __alter_pom_xml(self):
        """修改项目pom.xml文件"""
        # 将最外层的文件夹添加到新的元组中
        ttuple = (self.rootname,)
        ntuple = ttuple + \
                 tuple(self.module_name_list)
        default_artifactid_prefix = self.templatedict[self.series +
                                                      '.default_artifactid_prefix']
        default_group_id = self.templatedict[self.series + '.default_group_id']
        default_site_name = self.templatedict[self.series +
                                              '.default_site_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        for module_name in ntuple:
            module_name = module_name.replace('#', os.path.sep)
            pom_xml_file = ''
            # 如果元组内元素是项目名，文件路径需要特殊处理
            if module_name == self.rootname:
                pom_xml_file = os.path.join(self.rootpath, 'pom.xml')
            else:
                pom_xml_file = os.path.join(self.rootpath, module_name,
                                            'pom.xml')
            if not os.path.exists(pom_xml_file):
                continue
            try:
                with open(pom_xml_file, 'r',
                          encoding=self.DEFAULT_ENCODING) as xml_file, open(
                    '%s.bak' % pom_xml_file,
                    'w',
                    encoding=self.DEFAULT_ENCODING) as target_file:
                    self.messagehandle('正在修改：' + module_name + '/pom.xml')
                    for line in xml_file:
                        if default_group_id in line \
                                and ('<groupId>' in line
                                     or '<excludeGroupIds>' in line
                                     or '<includeGroupIds>' in line):
                            line = line.replace(default_group_id,
                                                self.groupid)
                        if default_artifactid_prefix in line and '<artifactId>' in line:
                            line = line.replace(
                                default_artifactid_prefix,
                                self.artifactid)
                        if '<module>' in line:
                            line = line.replace(
                                default_project_name,
                                self.projectname)
                        if module_name == self.rootname:
                            if '<name>' in line:
                                line = line.replace(
                                    default_project_name,
                                    self.projectname)
                            if 'version>' in line:
                                line = line.replace(
                                    default_artifactid_prefix,
                                    self.artifactid)
                            if 'description>' in line:
                                line = line.replace(
                                    default_site_name,
                                    self.sitename)
                        target_file.write(line)
                # 移除旧文件
                os.remove(pom_xml_file)
                # 重命名备份文件为新文件
                os.rename('%s.bak' % pom_xml_file, pom_xml_file)
            except Exception as err:
                self.exceptionhandle(
                    '修改项目pom.xml文件异常\n修改文件：{}\n异常信息：{}'.format(
                        pom_xml_file, err))

    def __alter_project_dir(self):
        """修改目录名"""
        default_dir_name = self.templatedict[self.series +
                                             '.default_dir_name']
        default_package_name = self.templatedict[self.series +
                                                 '.default_package_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        default_module_name_tuple = self.module_name_list
        # 保证父目录存在，否则会重命名失败
        base_dir = self.rootpath
        new_path_parent = base_dir.replace(default_project_name, self.projectname)
        if not os.path.exists(new_path_parent):
            os.makedirs(new_path_parent)
        # 缓存修改过的path
        path_map = {}
        for module_name in default_module_name_tuple:
            if module_name in path_map:
                continue
            real_module = ''
            raw_module = ''
            for m in module_name.split('#'):
                if raw_module != '':
                    raw_module += '#' + m
                else:
                    raw_module = m
                if m in path_map:
                    real_module = path_map[m]
                elif real_module != '':
                    real_module += '#' + m
                else:
                    real_module += m
                path_map[raw_module] = real_module.replace(default_project_name, self.projectname)
            module_name = real_module
            replace_module_name = module_name.replace('#', os.path.sep)
            old_module_path = os.path.join(self.rootpath, replace_module_name)
            new_module_path = os.path.join(self.rootpath,
                                           replace_module_name.replace(default_project_name, self.projectname))

            if not os.path.exists(old_module_path):
                continue
            src_main_java_dir = os.path.join(old_module_path, 'src/main/java')
            src_test_java_dir = os.path.join(old_module_path, 'src/test/java')

            # 测试路径
            if os.path.exists(src_test_java_dir):
                # 这里只修改包名
                source_dir = os.path.join(src_test_java_dir, self.packagename.replace('.', os.path.sep))
                old_dir = os.path.join(src_test_java_dir, default_package_name.replace('.', os.path.sep))
                if not os.path.exists(source_dir):
                    os.makedirs(source_dir)
                # 移动目录及文件
                self.move_dir(old_dir, source_dir)

                # 删除空目录
                for dir in os.listdir(src_test_java_dir):
                    dpath = os.path.join(src_test_java_dir, dir)
                    if os.path.isdir(dpath) and not os.listdir(dpath):
                        os.rmdir(dpath)

            # 修改源码
            if os.path.exists(src_main_java_dir):
                # 这里只修改包名
                source_dir = os.path.join(src_main_java_dir, self.packagename.replace('.', os.path.sep))
                old_dir = os.path.join(src_main_java_dir, default_package_name.replace('.', os.path.sep))
                if not os.path.exists(source_dir):
                    os.makedirs(source_dir)
                # 移动目录及文件
                self.move_dir(old_dir, source_dir)

                # 删除空目录
                for dir in os.listdir(src_main_java_dir):
                    dpath = os.path.join(src_main_java_dir, dir)
                    if not os.listdir(dpath):
                        os.rmdir(dpath)

            # 模块重命名，存不存在源码路径都要执行，否则嵌套模块会出问题
            base_dir = self.rootpath
            for d in module_name.split('#'):
                base_dir = os.path.join(base_dir, d)
                if not os.path.exists(base_dir):
                    continue
                new_base_dir = base_dir.replace(default_project_name, self.projectname)
                os.rename(base_dir, new_base_dir)
                base_dir = new_base_dir

            self.messagehandle('正在修改：' + replace_module_name)

        if (len(self.rootname) > 0):
            os.rename(self.rootpath,
                      os.path.join(self.targetdir, self.projectdirname))
            self.messagehandle('正在修改：' + self.rootname)
